package lxs.swift.collector.sync;

import lxs.swift.collector.wrap.Option;
import org.jetbrains.annotations.NotNull;

import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Consumer;
import java.util.function.Function;

public class RwLock<T> {
    private transient T value;
    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    private final Condition writeCondition = lock.writeLock().newCondition();

    public RwLock(T value) {
        this.value = value;
    }

//    public Condition readCondition() {
//        return lock.readLock().newCondition();
//    }
//
//    public Condition writeCondition() {
//        return lock.writeLock().newCondition();
//    }

    public T readGet() {
        lock.readLock().lock();
        try {
            return value;
        } finally {
            lock.readLock().unlock();
        }
    }

    public T readTryGet(long wait, @NotNull TimeUnit unit) {
        Objects.requireNonNull(unit);
        readLock(wait, unit);
        try {
            return value;
        } finally {
            lock.readLock().unlock();
        }
    }

    public void readUse(@NotNull Consumer<T> consumer) {
        Objects.requireNonNull(consumer);
        lock.readLock().lock();
        try {
            consumer.accept(value);
        } finally {
            lock.readLock().unlock();
        }
    }

    public void readTryUse(long wait, @NotNull TimeUnit unit, @NotNull Consumer<T> consumer) {
        Objects.requireNonNull(consumer);
        Objects.requireNonNull(unit);
        readLock(wait, unit);
        try {
            consumer.accept(value);
        } finally {
            lock.readLock().unlock();
        }
    }

    /**
     * 获取读锁,并拿到对象进行处理，返回处理后的结果
     */
    public <R> R readMap(@NotNull Function<T, R> f) {
        Objects.requireNonNull(f);
        lock.readLock().lock();
        try {
            return f.apply(value);
        } finally {
            lock.readLock().unlock();
        }
    }


    public <R> R readTryMap(long wait, @NotNull TimeUnit unit, @NotNull Function<T, R> f) {
        Objects.requireNonNull(unit);
        Objects.requireNonNull(f);
        readLock(wait, unit);
        try {
            return f.apply(value);
        } finally {
            lock.readLock().unlock();
        }
    }

    /**
     * 获取写锁,并消费对象
     */
    public void writeUse(@NotNull Consumer<T> consumer) {
        Objects.requireNonNull(consumer);
        lock.writeLock().lock();
        try {
            consumer.accept(value);
        } finally {
            unlockWrite();
        }
    }


    public void writeTryUse(long wait, @NotNull TimeUnit unit, @NotNull Consumer<T> f) {
        Objects.requireNonNull(unit);
        Objects.requireNonNull(f);
        writeLock(wait, unit);
        try {
            f.accept(value);
        } finally {
            unlockWrite();
        }
    }

    /**
     * 当更新成功时返回true,当其他线程正在更新时，立即返回false
     */
    public boolean writeUseIf(@NotNull Consumer<T> consumer) {
        Objects.requireNonNull(consumer);
        if (lock.isWriteLocked()) {
            return false;
        }
        this.writeUse(consumer);
        return true;
    }

    /**
     * 获取写锁,并映射对象
     */
    public <R> R writeMap(@NotNull Function<T, R> f) {
        Objects.requireNonNull(f);
        lock.writeLock().lock();
        try {
            return f.apply(value);
        } finally {
            unlockWrite();
        }
    }

    public <R> R writeTryMap(long wait, @NotNull TimeUnit unit, @NotNull Function<T, R> f) {
        Objects.requireNonNull(unit);
        Objects.requireNonNull(f);
        writeLock(wait, unit);
        try {
            return f.apply(value);
        } finally {
            unlockWrite();
        }
    }

    /**
     * 获取写锁,使用旧值得到新值,写入新值并返回新值
     */
    public T writeSet(@NotNull Function<T, T> f) {
        Objects.requireNonNull(f);
        lock.writeLock().lock();
        try {
            this.value = f.apply(value);
            return this.value;
        } finally {
            unlockWrite();
        }
    }

    public T writeTrySet(long wait, @NotNull TimeUnit unit, @NotNull Function<T, T> f) {
        Objects.requireNonNull(unit);
        Objects.requireNonNull(f);
        writeLock(wait, unit);
        try {
            this.value = f.apply(value);
            return this.value;
        } finally {
            unlockWrite();
        }
    }

    public Option<T> writeSoftSet(@NotNull Function<T, T> f) {
        Objects.requireNonNull(f);
        //如果已经有其他线程正在执行则阻塞并等待其他人返回值
        if (lock.isWriteLocked()) {
            return Option.none();
        }
        lock.writeLock().lock();
        try {
            this.value = f.apply(value);
            T t = this.value;
            return Option.some(t);
        } finally {
            unlockWrite();
        }
    }

    /**
     * 获取写锁,使用旧值得到新值,写入新值并返回老值
     */
    public T writeReplace(@NotNull Function<T, T> f) {
        Objects.requireNonNull(f);
        lock.writeLock().lock();
        try {
            T old = this.value;
            this.value = f.apply(value);
            return old;
        } finally {
            unlockWrite();
        }
    }

    public T writeTryReplace(long wait, @NotNull TimeUnit unit, @NotNull Function<T, T> f) {
        Objects.requireNonNull(unit);
        Objects.requireNonNull(f);
        writeLock(wait, unit);
        try {
            T old = this.value;
            this.value = f.apply(value);
            return old;
        } finally {
            unlockWrite();
        }
    }

    public Option<T> writeSoftReplace(@NotNull Function<T, T> f) {
        Objects.requireNonNull(f);
        //如果已经有其他线程正在执行则阻塞并等待其他人返回值
        if (lock.isWriteLocked()) {
            return Option.none();
        }
        lock.writeLock().lock();
        try {
            T old = this.value;
            this.value = f.apply(value);
            return Option.some(old);
        } finally {
            unlockWrite();
        }
    }

    public boolean isWriteLocked() {
        return lock.isWriteLocked();
    }

    public void writeUnlockAwait() {
        try {
            writeCondition.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private void readLock(long wait, @NotNull TimeUnit unit) {
        try {
            boolean b = lock.readLock().tryLock(wait, unit);
            if (!b) throw new RuntimeException("get value for read failed: wait time out");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void writeLock(long wait, @NotNull TimeUnit unit) {
        try {
            boolean b = lock.writeLock().tryLock(wait, unit);
            if (!b) throw new RuntimeException("get value for write failed: wait time out");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void unlockWrite() {
        if (!isWriteLocked()) return;
        writeCondition.signalAll();
        lock.writeLock().unlock();
    }
}
